﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using MongoDB.Bson;
using MongoDB.Driver;
using NLog;
using Quartz;
using OPCService.Utils;
using OPCService.Model;
using OPCAutomation;
using JSONDB.helpers;
using JSONDB.Model;
using System.Data.SQLite;
using OPCService.Service;
using JSONItem = JSONDB.Item; //别名
using OpcItem = OPCService.Model.Item;

namespace OPCService.Job
{
    class Opc800DataCollection: IJob
    {
        private static readonly Logger logger = NLog.LogManager.GetCurrentClassLogger();
        private static String groupName = ConfigHelper.ReadConfig("groupname");
        private static readonly String colName = ConfigHelper.ReadConfig("colname");
        private static readonly String indb = ConfigHelper.ReadConfig("indb");
        private static String opcname = ConfigHelper.ReadConfig("Opc800DataCollection");
        private static string jsonconfig = AppDomain.CurrentDomain.BaseDirectory + @ConfigHelper.ReadConfig("jsondb");
        private static readonly String sqliteName = ConfigHelper.ReadConfig("sqliteName");

        OPCServer server = new OPCServer();
        MongoDbHelper mongoDbHelper = new MongoDbHelper();
        JsonDBHelper jsonDBHelper = new JsonDBHelper(jsonconfig);

        static Dictionary<string, List<string>> dicIds = new Dictionary<string, List<string>>();
        static Dictionary<string, OPCServer> serverDic = new Dictionary<string, OPCServer>();
        static Dictionary<string, OPCGroup> groupDic = new Dictionary<string, OPCGroup>();
        static Dictionary<string, OPCItem> itemsDict = new Dictionary<string, OPCItem>();
        static Dictionary<string, Array> serverHandleDict = new Dictionary<string, Array>();

        #region 入口
        public async Task Execute(IJobExecutionContext context)
        {
            await Task.Run(async () =>
            {
                try
                {
                    mongoDbHelper.logger = logger;

                    //连接OPCServer
                    ConnectOpcServer();

                    //读取Tag配置文件
                    List<string> ids = await readTagFromTxt();

                    //读取数据并插入数据库
                    await getAndStoreValues(ids);
                }
                catch (Exception e)
                {
                    logger.Error(e.Message);
                    logger.Error(e.StackTrace);
                    if (server != null && serverDic.ContainsKey(opcname) && server.ServerState != (int)OPCServerState.OPCRunning)
                    {
                        serverDic.Remove(opcname);
                    }
                }
            });
        }
        #endregion

        #region OPC相关
        //连接OPC
        private bool ConnectOpcServer()
        {
            if (serverDic.Count == 0 || !serverDic.ContainsKey(opcname))
            {
                server.Connect(opcname, "");
                Thread.Sleep(100);

                logger.Debug("正在连接server:{}");
                if (server.ServerState == (int)OPCServerState.OPCRunning)
                {
                    serverDic.Add(opcname, server);
                    opcStatus(true);
                }
                else
                {
                    logger.Error("无法连接");
                    opcStatus(false);
                    return false;
                }
            }
            
            return true;
        }

        //创建组
        private bool AddGroup(string groupName)
        {
            try
            {
                OPCGroups opcGroups = server.OPCGroups;
                OPCGroup opcGroup = opcGroups.Add(groupName);
                server.OPCGroups.DefaultGroupIsActive = true;
                server.OPCGroups.DefaultGroupDeadband = 0;
                opcGroup.UpdateRate = 250;
                opcGroup.IsActive = true;
                opcGroup.IsSubscribed = true;
                opcGroup.DataChange += new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
                opcGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(KepGroup_AsyncWriteComplete);
                groupDic.Add(groupName, opcGroup);
            }
            catch (Exception err)
            {
                logger.Warn("创建组出现错误：" + err.Message);
                return false;
            }
            return true;
        }

        /// <summary>
        /// 写入TAG值时执行的事件
        /// </summary>
        /// <param name="TransactionID"></param>
        /// <param name="NumItems"></param>
        /// <param name="ClientHandles"></param>
        /// <param name="Errors"></param>
        void KepGroup_AsyncWriteComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
        {
            for (int i = 1; i <= NumItems; i++)
            {
                Console.WriteLine("Tran:" + TransactionID.ToString() + "   CH:" + ClientHandles.GetValue(i).ToString() + "   Error:" + Errors.GetValue(i).ToString());
            }
        }

        /// <summary>
        /// 每当项数据有变化时执行的事件
        /// </summary>
        /// <param name="TransactionID">处理ID</param>
        /// <param name="NumItems">项个数</param>
        /// <param name="ClientHandles">项客户端句柄</param>
        /// <param name="ItemValues">TAG值</param>
        /// <param name="Qualities">品质</param>
        /// <param name="TimeStamps">时间戳</param>
        void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
        {
            //为了测试，所以加了控制台的输出，来查看事物ID号
            List<OpcItem> values = new List<OpcItem>();
            for (int i = 1; i <= NumItems; i++)
            {
                OpcItem item = new OpcItem();
                item.tag_name = ClientHandles.GetValue(i).ToString();
                if (ItemValues.GetValue(i)?.GetType() == typeof(Boolean))
                {
                    item.value = ItemValues.GetValue(i).ToString() == "True" ? 1 : 0;
                }
                else
                {
                    item.value = double.Parse(ItemValues.GetValue(i).ToString());
                }
                    
                item.quality = Qualities.GetValue(i).ToString();
                item.datetime = Convert.ToDateTime(TimeStamps.GetValue(i).ToString());
                values.Add(item);
            }
        }
        #endregion

      
        #region 读取tag item
        private async Task<List<string>> readTagFromTxt()
        {
            List<string> ids = new List<string>();
            if (!dicIds.ContainsKey(opcname))
            {
                string @filename = $"{opcname}.txt";
                FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
                using (StreamReader reader = new StreamReader(fs, Encoding.Default))
                {
                    string id = reader.ReadLine();
                    while (id != null)
                    {
                        ids.Add(id);
                        id = reader.ReadLine();
                    }
                    reader.Close();
                }

                if (ids.Count == 0)
                {
                    logger.Warn("采集项目标签为0,请检查标签配置");
                }
                else if (ids.Count > 2000)
                {
                    logger.Warn("采集项目标签大于2000,请检查标签配置");
                }
                dicIds.Add(opcname, ids);
            }
            else
            {
                ids = dicIds[opcname];
            }
            return ids;
        }
        #endregion

        #region 获取和保存数据
        private async Task getAndStoreValues(List<string> ids)
        {
            if ("true".Equals(indb))
            {
                if (!groupDic.ContainsKey(groupName))
                {
                    AddGroup(groupName);
                    Thread.Sleep(100);
                }

                //获取最新值
                await getValues(ids);
            }
        }
        #endregion

        #region 插入mangodb数据库
        //获取value并插入mangodb数据库（每次单条读取）
        private async Task getValuesSingle(List<string> ids)
        {
            if(ids.Count == 0)
            {
                logger.Warn("配置文件为空");
                return;
            }
            List<OpcItem> queueItem = new List<OpcItem>();
            OPCGroup group = groupDic.FirstOrDefault(f => f.Key == groupName).Value;
            if (group.OPCItems.Count == 0)//如果有item不再增加,表示已经加入过
            {
                OPCItems opcItems = group.OPCItems;
                for (int i = 0; i < ids.Count; i++)
                {
                    int itmHandleClient = i + 1;
                    OPCItem opcItem = opcItems.AddItem(ids[i], itmHandleClient);
                    itemsDict.Add(ids[i], opcItem);
                }
            }

            string collName = colName;
            int count = 0;
            foreach (string id in ids)
            {
                if (itemsDict.TryGetValue(id, out OPCItem opcItem))
                {
                    opcItem.Read(1, out object value, out object quality, out object timeStamp);
                    if (value == null)
                    {
                        continue;
                    }

                    double v = 0.00;
                    if (value?.GetType() == typeof(Boolean))
                    {
                        v = value.ToString() == "True" ? 1 : 0;
                    }
                    else
                    {
                        v = Double.Parse(value?.ToString());
                    }
                    
                    try
                    {
                        OpcItem itemTag = new OpcItem
                        {
                            tag_name = id,
                            description = "",
                            value = v,
                            //datetime = Convert.ToDateTime(timeStamp.ToString()),//该时间为OPC获取到的时间
                            datetime = DateTime.Now, //改为记录当前时间
                            quality = quality.ToString()
                        };

                        queueItem = TagsQueue.GetQueueFromTagName(itemTag);
                        if (queueItem.Count > 0)
                        {
                            logger.Debug($"入库{queueItem.Count}条");
                            foreach (var item in queueItem)
                            {
                                BsonDocument document = item.ToBsonDocument();
                                var result = mongoDbHelper.Insert(collName, document);
                                logger.Debug("插入item{}", item.tag_name);
                            }
                            count += queueItem.Count;
                            queueItem.Clear();
                        }
                    }
                    catch (Exception e)
                    {
                        logger.Error(e.Message);
                        logger.Error(e.StackTrace);
                    }
                    finally
                    {

                    }
                }
            }
            if (count > 0)
            {
                logger.Info($"{group.Name}数据已新增{count}条");
            }
        }

        //获取value并插入mangodb数据库
        private async Task getValues(List<string> ids)
        {
            if (ids.Count == 0)
            {
                logger.Warn("配置文件为空");
                return;
            }
            List<OpcItem> queueItem = new List<OpcItem>();
            OPCGroup group = groupDic.FirstOrDefault(f => f.Key == groupName).Value;

            // 1、 创建OPC项集合及初始化参数
            Array itemIDs = Array.CreateInstance(typeof(string), ids.Count + 1);  // 创建基于1的索引数组
            Array clientHandles = Array.CreateInstance(typeof(int), ids.Count + 1);
            Array serverHandles;
            Array errors;
            for (int i = 1; i <= ids.Count; i++)
            {
                itemIDs.SetValue(ids[i-1], i);      // 索引从1开始
                clientHandles.SetValue(i, i);       // 设置客户端句柄（从1开始）,句柄值 = 索引值
            }

            //2、批量添加项
            if (group.OPCItems.Count == 0)
            {
                OPCItems opcItems = group.OPCItems;
                //如果有item不再增加,表示已经加入过
                opcItems.AddItems(
                    ids.Count,
                    ref itemIDs,   // 输出客户端句柄
                    ref clientHandles,
                    out serverHandles,
                    out errors
                );

                //检查添加错误(基于1的索引)
                for (int i = 1; i <= errors.Length; i++)
                {
                    if ((int)errors.GetValue(i) != 0)
                    {
                        string tag = (string)itemIDs.GetValue(i);
                        Console.WriteLine($"添加标签失败: {tag}, 错误码: {errors.GetValue(i)}");
                    }
                }

                int index = 0;
                foreach (OPCItem item in opcItems)
                {
                    itemsDict.Add(ids[index], item); //添加到字典
                    index++;
                }
                serverHandleDict.Add(groupName, serverHandles);
                Thread.Sleep(100);
            }

            //3、执行同步批量读取
            Array itemValues = Array.CreateInstance(typeof(object), ids.Count);
            Array itemErrors = Array.CreateInstance(typeof(int), ids.Count);
            Array readServerHandles = serverHandleDict.FirstOrDefault(f => f.Key == groupName).Value;
            object itemQualities = null;
            object itemTimeStamps = null;
            group.SyncRead(
                (short)OPCDataSource.OPCDevice, // 从设备读取
                ids.Count,
                readServerHandles,  // 使用服务器句柄
                out itemValues,
                out itemErrors,
                out itemQualities,
                out itemTimeStamps
            );

            //4、处理读取结果
            Array qualitiesArray = (Array)itemQualities;
            Array timeStampsArray = (Array)itemTimeStamps;
            string collName = colName;
            int count = 0;
            int total;
            List<JSONItem> items = jsonDBHelper.GetItems(opcname, 200, 1, out total);
            for (int i = 1; i <= ids.Count; i++)
            {
                string tag = (string)itemIDs.GetValue(i);
                if (string.IsNullOrWhiteSpace(tag))
                {
                    continue;
                }

                int errorCode = (int)itemErrors.GetValue(i);
                if (errorCode != 0)
                {
                    Console.WriteLine($"[读取失败] 标签: {tag}, 错误码: {errorCode}");
                }
                else
                {
                    Console.WriteLine($"[读取成功] 标签: {tag}, " +  $"值: {itemValues.GetValue(i)}, " + $"质量: {qualitiesArray.GetValue(i)}");
                    object value = itemValues.GetValue(i);
                    if (value == null)
                    {
                        continue;
                    }

                    double v = 0.00;
                    if (value?.GetType() == typeof(Boolean))
                    {
                        v = value.ToString() == "True" ? 1 : 0;
                    }
                    else
                    {
                        v = Double.Parse(value?.ToString());
                    }

                    try
                    {
                        await calculatePeriiod(items, tag, v);  //计算周期
                        OpcItem itemTag = new OpcItem
                        {
                            tag_name = tag,
                            description = "",
                            value = v,
                            //datetime = Convert.ToDateTime(timeStampsArray.GetValue(i).ToString()),//该时间为OPC获取到的时间
                            datetime = DateTime.Now, //改为记录当前时间
                            quality = qualitiesArray.GetValue(i).ToString()
                        };
                        //每个Tag一个队列，队列满了才一次性插入数据库
                        queueItem = TagsQueue.GetQueueFromTagName(itemTag);
                        if (queueItem.Count > 0)
                        {
                            logger.Debug($"入库{queueItem.Count}条");
                            foreach (var item in queueItem)
                            {
                                BsonDocument document = item.ToBsonDocument();
                                var result = mongoDbHelper.Insert(collName, document);
                                logger.Debug("插入item{}", item.tag_name);
                            }
                            count += queueItem.Count;
                            queueItem.Clear();
                        }
                    }
                    catch (Exception e)
                    {
                        logger.Error(e.Message);
                        logger.Error(e.StackTrace);
                    }
                    finally
                    {

                    }
                }
            }
            
            if (count > 0)
            {
                logger.Info($"{group.Name}数据已新增{count}条");
            }
        }

        //写数据
        private async Task WriteData(List<string> ids)
        {
            foreach (string id in ids)
            {
                if (itemsDict.TryGetValue(id, out OPCItem opcItem))
                {
                    opcItem.Read(1, out object value, out object quality, out object timeStamp);
                    if (value == null)
                    {
                        continue;
                    }

                    double v = 0.00;
                    if (value?.GetType() == typeof(Boolean))
                    {
                        v = value.ToString() == "True" ? 1 : 0;
                        if (v == 0)
                        {
                            opcItem.Write(1);
                        }
                        else
                        {
                            opcItem.Write(0);
                        }
                    }
                    else
                    {
                        v = Double.Parse(value?.ToString());
                        if (v >= 1050)
                        {
                            v = 850;
                        }
                        else
                        {
                            v++;
                        }
                        opcItem.Write(v);
                    }
                }
            }
        }

        //计算周期
        private async Task calculatePeriiod(List<JSONItem> items, string tag_name, double value)
        {
            foreach(JSONItem item in items)
            {
                if (item.period == "是" && tag_name == item.value)
                {
                    // 创建构建器
                    var builder = Builders<Item>.Filter;

                    // 构建排序方式：按 datetime 倒序
                    var sort = Builders<OpcItem>.Sort.Descending(x => x.datetime);

                    FilterDefinition<OpcItem> filter = builder.Eq("tag_name", item.value);
                    List<OpcItem> datalist = mongoDbHelper.findLimit<OpcItem>(colName, filter, sort, 3);
                    //如果上一个值为1，当前值为0时
                    if (datalist.Count > 0 && value.Equals(0) && datalist[0].value.Equals(1))
                    {
                        using (var db = new SQLiteService(sqliteName))
                        {
                            // 创建表
                            db.CreateTableIfNotExists("Period",
                                "Id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                                "TagName TEXT UNIQUE NOT NULL, " +
                                "Period INTEGER, " +
                                "StartTime DATETIME DEFAULT NULL, " +
                                "EndTime DATETIME DEFAULT NULL");

                            // 查询数据
                            var query = "SELECT * FROM Period WHERE TagName = @tagname";
                            var parameters = new Dictionary<string, object> {
                                { "@tagname", item.value }
                            };
                            DataTable result = db.ExecuteQuery(query, parameters);
                            if (result.Rows.Count > 0)
                            {
                                DateTime StartTime;
                                //是否为空
                                if (result.Rows[0]["EndTime"] == DBNull.Value)
                                {
                                    StartTime = Convert.ToDateTime(result.Rows[0]["StartTime"].ToString());
                                }
                                else
                                {
                                    StartTime = Convert.ToDateTime(result.Rows[0]["EndTime"].ToString());//开始时间为上一个周期的结束时间
                                }

                                // 计算时间差（秒）
                                DateTime EndTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                                TimeSpan difference = EndTime - StartTime;
                                double seconds = difference.TotalSeconds;

                                // 更新数据
                                var updateData = new Dictionary<string, object> {
                                    { "Period", seconds },
                                    { "StartTime", StartTime },
                                    { "EndTime", EndTime }
                                };
                                db.Update("Period", updateData, "TagName = @tagname", new Dictionary<string, object> { { "@tagname", item.value } });
                            }
                            else
                            {
                                DateTime createTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                                // 插入数据
                                var rowData = new Dictionary<string, object>
                                {
                                    { "TagName", item.value },
                                    { "Period", 0 },
                                    { "StartTime", createTime }
                                };
                                db.Insert("Period", rowData);
                            }
                        }
                    }
                }
            }
        }

        //OPC状态更新
        private void opcStatus(bool value)
        {
            using (var db = new SQLiteService(sqliteName))
            {
                // 创建表
                db.CreateTableIfNotExists("CheckStatus",
                    "Id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                    "Name TEXT UNIQUE NOT NULL, " +
                    "Status BOOLEAN DEFAULT FALSE");

                // 查询数据
                var query = "SELECT * FROM CheckStatus WHERE Name = @name";
                var parameters = new Dictionary<string, object> {
                    { "@name", opcname }
                };
                DataTable result = db.ExecuteQuery(query, parameters);
                if (result.Rows.Count > 0)
                {
                    // 更新数据
                    var updateData = new Dictionary<string, object> {
                        { "Status", value }
                    };
                    db.Update("CheckStatus", updateData, "Name = @name", new Dictionary<string, object> { { "@name", opcname } });
                }
                else
                {
                    // 插入数据
                    var rowData = new Dictionary<string, object>
                    {
                        { "Name", opcname },
                        { "Status", value }
                    };
                    db.Insert("CheckStatus", rowData);
                }
            }
        }
        #endregion
    }
}
